/* ****************************************************************** **
**    OpenSees - Open System for Earthquake Engineering Simulation    **
**          Pacific Earthquake Engineering Research Center            **
** ****************************************************************** */

//
// 文件: GPUMaterialExtractor.cpp
// 说明: GPU材料参数提取器实现
//

#include "GPUMaterialExtractor.h"
#include <Element.h>
#include <FourNodeQuad.h>
#include <NDMaterial.h>
#include <ElasticIsotropicMaterial.h>
#include <PlasticDamageConcrete3d.h>
#include <PlaneStressMaterial.h>
#include <iostream>

/**
 * @brief 从Quad单元提取材料参数
 */
int GPUMaterialExtractor::extractQuadMaterial(Element* theElement, GPUElementData& elemData)
{
    // 尝试转换为FourNodeQuad
    FourNodeQuad* quadElem = dynamic_cast<FourNodeQuad*>(theElement);

    if (quadElem == nullptr) {
        std::cerr << "[GPUMaterialExtractor] Element is not FourNodeQuad" << std::endl;
        return -1;
    }

    // ====== 1. 提取几何参数 ======
    // FourNodeQuad已声明GPUSolver为friend，可以访问私有成员
    // 将thickness放在固定位置[8]，避免与材料参数冲突
    elemData.materialProps[8] = quadElem->thickness;  // 厚度

    // ====== 2. 提取材料参数 ======
    // 通过公共方法getMaterials()获取材料对象
    NDMaterial** materials = quadElem->getMaterials();

    if (materials == nullptr || materials[0] == nullptr) {
        std::cerr << "[GPUMaterialExtractor] No material found in element" << std::endl;
        return -1;
    }

    NDMaterial* mat = materials[0];  // 使用第一个积分点的材料

    // 从材料中提取密度（而不是从单元）
    // 将rho放在固定位置[9]
    elemData.materialProps[9] = mat->getRho();  // 密度

    // ====== 3. 根据材料类型提取参数 ======
    const char* matType = mat->getType();
    const char* matClass = mat->getClassType();

    // 调试输出：显示材料类型信息 (已禁用以提升性能)
    // std::cout << "[GPUMaterialExtractor] Material type check - Type: '" << matType
    //           << "', Class: '" << matClass << "'" << std::endl;

    // ====== 解包 PlaneStress/PlaneStrain wrapper ======
    // OpenSees 的 Quad 单元使用 PlaneStressMaterial 包装 3D 材料
    NDMaterial* actualMaterial = mat;
    PlaneStressMaterial* planeStressMat = dynamic_cast<PlaneStressMaterial*>(mat);

    if (planeStressMat != nullptr) {
        // 获取内部的3D材料
        actualMaterial = planeStressMat->getTheMaterial();
        if (actualMaterial != nullptr) {
            // std::cout << "[GPUMaterialExtractor] Unwrapped PlaneStressMaterial, "
            //           << "inner material type: '" << actualMaterial->getType() << "'" << std::endl;
        }
    }

    // ====== 检测材料类型 ======
    // 优先使用 dynamic_cast 检测 PlasticDamageConcrete3d（最可靠）
    PlasticDamageConcrete3d* concreteMat = dynamic_cast<PlasticDamageConcrete3d*>(actualMaterial);

    if (concreteMat != nullptr) {
        // 成功转换为 PlasticDamageConcrete3d
        elemData.materialType = static_cast<int>(GPUMaterialType::PLASTIC_DAMAGE_CONCRETE_3D);

        // 使用getter方法提取材料参数
        elemData.materialProps[0] = concreteMat->getE();      // E (弹性模量)
        elemData.materialProps[1] = concreteMat->getNu();     // nu (泊松比)
        elemData.materialProps[2] = concreteMat->getFt();     // ft (抗拉强度)
        elemData.materialProps[3] = concreteMat->getFc();     // fc (抗压强度)
        elemData.materialProps[4] = concreteMat->getBeta();   // beta (塑性流动参数)
        elemData.materialProps[5] = concreteMat->getAp();     // Ap (拉伸损伤参数)
        elemData.materialProps[6] = concreteMat->getAn();     // An (压缩损伤参数)
        elemData.materialProps[7] = concreteMat->getBn();     // Bn (压缩损伤指数)

        // std::cout << "[GPUMaterialExtractor] Quad[" << theElement->getTag()
        //           << "] PlasticDamageConcrete3d: E=" << elemData.materialProps[0]
        //           << ", nu=" << elemData.materialProps[1]
        //           << ", ft=" << elemData.materialProps[2]
        //           << ", fc=" << elemData.materialProps[3]
        //           << ", beta=" << elemData.materialProps[4]
        //           << ", Ap=" << elemData.materialProps[5]
        //           << ", An=" << elemData.materialProps[6]
        //           << ", Bn=" << elemData.materialProps[7] << std::endl;

    } else if (strcmp(matType, "PlaneStress") == 0 ||
               strcmp(matType, "PlaneStrain") == 0 ||
               strcmp(matType, "ThreeDimensional") == 0 ||
               strcmp(matType, "AxiSymmetric") == 0) {

        // 弹性各向同性材料 (ElasticIsotropic的各种子类)
        elemData.materialType = static_cast<int>(GPUMaterialType::ELASTIC_ISOTROPIC);

        // 通过初始切线刚度矩阵提取E和nu
        const Matrix& C = mat->getInitialTangent();

        // 从刚度矩阵反推E和nu
        // C(0,0) = E/(1-nu²)  (平面应力)
        // C(0,1) = E*nu/(1-nu²)
        if (C.noRows() >= 3 && C.noCols() >= 3) {
            double C00 = C(0,0);
            double C01 = C(0,1);

            if (fabs(C00) > 1e-10) {
                double nu = C01 / C00;
                double E = C00 * (1.0 - nu * nu);

                elemData.materialProps[0] = E;
                elemData.materialProps[1] = nu;

                // std::cout << "[GPUMaterialExtractor] Quad[" << theElement->getTag()
                //           << "] ElasticIsotropic: E=" << E << ", nu=" << nu
                //           << ", t=" << elemData.materialProps[8]
                //           << ", rho=" << elemData.materialProps[9] << std::endl;
            }
        }

    } else {
        // 未知材料类型，使用默认弹性
        std::cerr << "[GPUMaterialExtractor] Warning: Unknown material type '"
                  << matType << "', using elastic default" << std::endl;

        elemData.materialType = static_cast<int>(GPUMaterialType::ELASTIC_ISOTROPIC);
        elemData.materialProps[0] = 3.0e10;   // E
        elemData.materialProps[1] = 0.2;      // nu
        // thickness and rho already extracted above

        return -1;
    }

    return 0;
}

/**
 * @brief 从Truss单元提取材料参数
 */
int GPUMaterialExtractor::extractTrussMaterial(Element* theElement, GPUElementData& elemData)
{
    // TODO: 实现Truss材料提取
    elemData.materialProps[0] = 2.0e11;  // E = 200 GPa
    elemData.materialProps[1] = 0.01;    // A = 0.01 m²
    elemData.materialProps[2] = 7850.0;  // rho = 7850 kg/m³

    return 0;
}

/**
 * @brief 从Beam单元提取材料参数
 */
int GPUMaterialExtractor::extractBeamMaterial(Element* theElement, GPUElementData& elemData)
{
    // TODO: 实现Beam材料提取
    elemData.materialProps[0] = 2.0e11;
    elemData.materialProps[1] = 8.0e10;
    elemData.materialProps[2] = 0.01;
    elemData.materialProps[3] = 1.0e-4;
    elemData.materialProps[4] = 7850.0;

    return 0;
}
